package com.base.aspect;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import com.base.dialects.DialectFactory;
import com.base.dialects.DialectRegistry;
import com.base.dialects.Dialects;
import com.base.dialects.YoYSqlBuildUtil;
import com.base.vo.Field;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.SQLException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * @author wnhuang
 * @Package com.base.aspect
 * @date 2021/7/8 0:24
 */
@Slf4j
@NoArgsConstructor
@SuppressWarnings({"rawtypes"})
public class YoYQueryInnerInterceptor implements InnerInterceptor {

    @Override
    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        return true;
    }

    @SneakyThrows
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        String sqlId = ms.getId();//获得mybatis的*mapper.xml文件中映射的方法，如：com.best.dao.UserMapper.selectById
        if (!sqlId.endsWith("selectYoYBySql")) {
            return;
        }
        Configuration configuration = ms.getConfiguration();
        DbType dbType = JdbcUtils.getDbType(executor);
        chargeSql(boundSql, configuration , dbType);
        chargeParameter(boundSql, configuration);
    }

    private void chargeSql(BoundSql boundSql,Configuration configuration,DbType dbType) throws NoSuchFieldException, IllegalAccessException {
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        Object parameterObject = boundSql.getParameterObject();
        MetaObject metaObject = configuration.newMetaObject(parameterObject);
        List<Field> fieldList = (List<Field>) metaObject.getValue("ew.selectList");
        Dialects dialect = new DialectRegistry().getDialect(dbType);
        String newSql = dialect.buildYoYSql(sql,fieldList);
        log.debug("YoySql is :" + newSql);
        //通过反射修改sql语句
        java.lang.reflect.Field field = boundSql.getClass().getDeclaredField("sql");
        field.setAccessible(true);
        field.set(boundSql, newSql);
    }

    private void chargeParameter(BoundSql boundSql, Configuration configuration) {
        //获得参数对象，如{id:1,name:"user1",param1:1,param2:"user1"}
        Object parameterObject = boundSql.getParameterObject();
        //获得映射的对象参数
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        List<ParameterMapping> list = new ArrayList<>(parameterMappings.size());
        MetaObject metaObject = configuration.newMetaObject(parameterObject);
        if (parameterMappings.size() > 0 && parameterObject != null) {
            parameterMappings.forEach(t -> {
                String propertyName = t.getProperty();//获得属性名，如id,name等字符串
                if (metaObject.hasGetter(propertyName)) {//检查该属性是否在metaObject中
                    Object obj = metaObject.getValue(propertyName);//如果在metaObject中，那么直接获取对应的值
                    if (obj instanceof LocalDate) {
                        LocalDate localDate = (LocalDate) obj;
                        String newPropertyName = UUID.randomUUID().toString();
                        metaObject.setValue(newPropertyName, LocalDate.of(localDate.getYear(), localDate.getMonth(), localDate.getDayOfMonth()).minusDays(365));
                        list.add(new ParameterMapping.Builder(configuration, newPropertyName, LocalDate.class).build());
                    } else {
                        list.add(t);
                    }
                }
            });
            parameterMappings.addAll(list);
        }
    }
}
